/*
 * Created: Author: Aléx Carvalho / Date: Feb 18, 2015
 * 
 * Changed: $Author: ac280593@gmail.com $ / $Date: 2015-06-11 01:31:49 -0300 (Thu, 11 Jun 2015) $
 */
package com.cpm.view.zk.treetable;

import com.cpm.view.Element;
import com.cpm.view.zk.tree.DefaultTreeModel;
import com.cpm.view.zk.tree.DefaultTreeNode;
import com.cpm.view.zk.tree.TreeRender;
import com.cpm.wrapper.SolidObjects;
import java.util.Enumeration;
import java.util.UUID;
import org.zkoss.zk.ui.HtmlBasedComponent;
import org.zkoss.zk.ui.event.Event;
import org.zkoss.zk.ui.event.Events;
import org.zkoss.zul.Menuitem;
import org.zkoss.zul.Menupopup;
import org.zkoss.zul.Tree;
import org.zkoss.zul.Treecols;
import org.zkoss.zul.Treeitem;
import org.zkoss.zul.TreeitemRenderer;
import org.zkoss.zul.Treerow;

public abstract class AbstractTreeTable<T> implements Element {

    protected abstract void buildRootNode(DefaultTreeNode<T> root);

    protected abstract void render(Treerow row, DefaultTreeNode node, int i);

    private TreeRender treeRender;

    private Treecols cols = null;

    private final Tree tree = new Tree();

    public AbstractTreeTable() {
//        tree.setWidth("100%");
        tree.setHflex("true");
        tree.setVflex("true");
        defaultRender();
    }

    private void defaultRender() {
        treeRender = (Treeitem item, DefaultTreeNode node, int i) -> {
            item.setValue(node);

            //TODO: Implementar popup
            Menupopup p = new Menupopup();
            p.setId(UUID.randomUUID().toString());
            Menuitem i1 = new Menuitem("Create New");
            Menuitem i2 = new Menuitem("Delete");
            p.appendChild(i1);
            p.appendChild(i2);
            p.setPage(tree.getPage());
            item.setContext(p.getId());

            Treerow row = item.getTreerow();

            if (row == null) {
                row = new Treerow();

                row.setParent(item);
            }
            
            render(row, node, i);
        };
    }

    public void buildTree() {

        if (cols == null) {
            cols = new Treecols();
        }
        else {
            cols.getChildren().clear();
        }
        
        buildHeader(cols);

        tree.appendChild(cols);

        DefaultTreeNode root = new DefaultTreeNode("ROOT");

        DefaultTreeModel model = new DefaultTreeModel(root);
        
        buildRootNode(root);

        tree.setItemRenderer(new DefaultTreeTableItemRenderer());
        tree.setModel(model);
    }

    /**
    * Sobrescrever conforme necessidade
     * @param cols
    */
    protected void buildHeader(Treecols cols){
    }
    
    public DefaultTreeNode<T> getSelectedNode() {
        if (tree.getModel() instanceof DefaultTreeModel) {
            DefaultTreeModel model = (DefaultTreeModel) tree.getModel();

            if (!model.getSelection().isEmpty()) {
                return (DefaultTreeNode) model.getSelection().iterator().next();
            }
        }
        return null;
    }

    public void setSelectedNode(DefaultTreeNode<T> value) {
        if (tree.getModel() instanceof DefaultTreeModel) {
            DefaultTreeModel model = (DefaultTreeModel) tree.getModel();

            model.addToSelection(value);
        }
    }

    public T getSelectedValue() {
        DefaultTreeNode<T> node = getSelectedNode();

        return node != null ? node.getData() : null;
    }

    public void setSelectedValue(T value) {
        if (tree.getModel() instanceof DefaultTreeModel) {
            for (Treeitem treeitem : tree.getItems()) {
                Object next = treeitem.getValue();
                if (next instanceof DefaultTreeNode) {
                    DefaultTreeNode node = (DefaultTreeNode) next;
                    if (value.equals(node.getData())) {
                        setSelectedNode(node);
                        break;
                    }
                }
            }
        }
    }

    public void expand() {
        if (tree.getModel() instanceof DefaultTreeModel) {
            expandNode((DefaultTreeNode<T>) tree.getModel().getRoot());
        }
    }

    public void expandAll() {
        if (tree.getModel() instanceof DefaultTreeModel) {
            DefaultTreeModel model = (DefaultTreeModel) tree.getModel();

            DefaultTreeNode root = (DefaultTreeNode) tree.getModel().getRoot();

            for (Enumeration<DefaultTreeNode> e = root.breadthFirstEnumeration(); e.hasMoreElements();) {
                DefaultTreeNode node = e.nextElement();

                if (!node.isLeaf()) {
                    model.addOpenObject(node);
                }
            }
        }
    }

    public void expandNode(DefaultTreeNode<T> value) {
        DefaultTreeNode node = (DefaultTreeNode) value;

        if (node != null && tree.getModel() instanceof DefaultTreeModel) {
            DefaultTreeModel model = (DefaultTreeModel) tree.getModel();

            if (node.getParent() == null || model.isObjectOpened(node)) {
                for (Enumeration<DefaultTreeNode> e = node.children(); e.hasMoreElements();) {
                    expandNode(e.nextElement());
                }
            }
            else if (!node.isLeaf()) {
                model.addOpenObject(node);
            }
        }
    }

    public void collapse() {
        if (tree.getModel() instanceof DefaultTreeModel) {
            collapseNode((DefaultTreeNode<T>) tree.getModel().getRoot());
        }
    }

    public void collapseAll() {
        if (tree.getModel() instanceof DefaultTreeModel) {
            DefaultTreeNode root = (DefaultTreeNode) tree.getModel().getRoot();

            for (Enumeration<DefaultTreeNode> e = root.breadthFirstEnumeration(); e.hasMoreElements();) {
                DefaultTreeNode node = e.nextElement();

                if (!node.isLeaf()) {
                    collapseNode(node);
                }
            }
        }
    }

    public void collapseNode(DefaultTreeNode<T> value) {
        DefaultTreeNode node = (DefaultTreeNode) value;
        if (node != null && tree.getModel() instanceof DefaultTreeModel) {
            DefaultTreeModel model = (DefaultTreeModel) tree.getModel();

            if (node.getParent() == null || model.isObjectOpened(node)) {
                boolean expanded = false;

                for (Enumeration<DefaultTreeNode> e = node.children(); e.hasMoreElements();) {
                    DefaultTreeNode child = e.nextElement();

                    if (model.isObjectOpened(child)) {
                        collapseNode(child);

                        expanded = true;
                    }
                }

                if (!expanded) {
                    model.removeOpenObject(node);
                }
            }
        }
    }

    public void hiddenIconOpenCloseTree() {
        tree.setWidgetListener("onBind", "hiddenIconOpenCloseNodeChilds(this);");
    }

    public void openNodeOnSelect() {
        tree.addEventListener(Events.ON_SELECT, (Event e) -> {
            if (tree.getModel() instanceof DefaultTreeModel) {
                DefaultTreeNode node = (DefaultTreeNode) getSelectedNode();

                if (!node.isLeaf()) {
                    DefaultTreeModel model = (DefaultTreeModel) tree.getModel();

                    model.clearOpen();
                    model.addOpenObject(node);
                }
            }
        });
    }

    @Override
    public HtmlBasedComponent getElement() {
        return tree;
    }

    private class DefaultTreeTableItemRenderer implements TreeitemRenderer<DefaultTreeNode> {

        @Override
        public void render(Treeitem item, DefaultTreeNode node, int i) throws Exception {
            SolidObjects.notNull(treeRender);
            treeRender.render(item, node, i);
        }
    }
}
